package org.noote.libs.computervision.detection.blob;

import java.util.ArrayList;

import org.noote.libs.computervision.buffer.Buffer8I;
import org.noote.libs.computervision.buffer.BufferShort;

// http://en.wikipedia.org/wiki/Blob_extraction
// http://code.google.com/p/blobscanner/source/browse/trunk/Blobscanner_034/src/Blobscanner/Detector.java

public class TwoPassDetection {

	private ArrayList<BlobSpace> detectedBlobs = new ArrayList<BlobSpace>();
	
	private final int[] dx = {-1, 0, 1,-1};
	private final int[] dy = {-1,-1,-1, 0};
	
	protected BufferShort groups = new BufferShort(), labels = new BufferShort();
	
	private class InnerLabel
	{
		int acc = 0;
		int min_x, max_x;
		int min_y, max_y;

		public InnerLabel(final int width, final int height)
		{
			min_x=width; max_x=0;
			min_y=height; max_y=0;			
		}
	};
	
	private ArrayList<InnerLabel> detectedLabels = new ArrayList<InnerLabel>();	
	
	public boolean detect(final Buffer8I image, final int threshold, final ArrayList<BlobSpace> blobs, final boolean getMask, final int blobMinSize)
	{
		if(!create(image.getWidth(), image.getHeight()))
			return false;
		
		long countBlobPixel = 0;
		
		for(int y=0; y<image.getHeight(); y++)
		{
			for(int x=0; x<image.getWidth(); x++)
			{
				if(image.getPixel(x, y)>threshold)
				{
					groups.drawPixel(x, y, 1);
					countBlobPixel++;
				}
				else
				{
					groups.drawPixel(x, y, 0);
				}
			}
		}
		
		if(countBlobPixel == 0) return false;
		
		doLabels();
		buildLabels();
		buildBlobs();
		
		int blobCount = 0;
		for(BlobSpace blob : detectedBlobs)
		{
			if(blob.size_x < blobMinSize || blob.size_y < blobMinSize) continue;
			
			if(getMask)
			{
				if(!extractMaskFrom(blob, image)) continue;
			}
			
			blobs.add(blob);			
		}
		
		return blobCount==0?false:true;
	}
	
	private boolean create(final int width, final int height)
	{
		if(!groups.createBuffer(width, height)) return false;
		groups.fill(0);
		
		if(!labels.createBuffer(width, height))return false;
		labels.fill(0);
		
		return true;
	}

	private void doLabels()
	{
		int idLabel = 1;
		int sumTest = 0;
		int smallLabel = 0, labelLocal = 0;
		
		detectedLabels.add(new InnerLabel(labels.getWidth(), labels.getHeight()));

		// first pass !
		for(int y=0; y<labels.getHeight(); y++)
		{
			for(int x=0; x<labels.getWidth(); x++)
			{
				if(groups.getPixel(x, y) != 1) continue;
				
				sumTest = 0;
				for (int i = 0; i < 4; i++)
                {
                    sumTest += labels.getPixel(x + dx[i], y + dy[i]);
                }
				
                //if the local sum is 0
                if (sumTest == 0)
                {
                    //assign new label
                	labels.drawPixel(x,  y, idLabel);
                    //increment label
                	idLabel++;
                	detectedLabels.add(new InnerLabel(labels.getWidth(), labels.getHeight()));
                }
                else
                {
                    //find the smallest label...
                    for (int i = 0; i < 4; i++)
                    {
                    	labelLocal = labels.getPixel(x + dx[i], y + dy[i]);
                        if(i == 0) smallLabel = labelLocal;
                        else if(labelLocal<smallLabel) smallLabel = labelLocal;
                    }

                    //label the matrix space...
                    for (int i = 0; i < 4; i++)
                    {
                        if (groups.getPixel(x + dx[i], y + dy[i]) == 1)
                        {
                        	labels.drawPixel(x + dx[i], y + dy[i], smallLabel);
                        }
                    }
                    //..and finally assign the  label to the pixel @ (x,y) position
                    labels.drawPixel(x, y, smallLabel);
                }
			}
		}
		
		// second pass !
		for(int y=labels.getHeight()-1; y>0; y--)
		{
			for(int x=labels.getWidth()-1; x>0; x--)
			{
				if(labels.getPixel(x, y) == 0) continue;

                //find the smallest label...
                for (int i = 0; i < 4; i++)
                {
                	labelLocal = labels.getPixel(x + dx[i], y + dy[i]);
                    if(i == 0) smallLabel = labelLocal;
                    else if(labelLocal<smallLabel) smallLabel = labelLocal;
                }

                //label the matrix space...
                for (int i = 0; i < 4; i++)
                {
                    if (labels.getPixel(x + dx[i], y + dy[i]) != 0 && groups.getPixel(x + dx[i], y + dy[i]) == 1)
                    {
                    	labels.drawPixel(x + dx[i], y + dy[i], smallLabel);
                    }
                }
			}
		}
	}
	
	private void buildLabels()
	{
		// attach labels properties
		for(int y=0; y<labels.getHeight(); y++)
		{
			for(int x=0; x<labels.getWidth(); x++)
			{
				int whatLabel = labels.getPixel(x, y);
				
				InnerLabel label = detectedLabels.get(whatLabel);
				label.acc++;
				if(label.max_x>x) label.max_x=x;
				if(label.min_x<x) label.min_x=x;
				if(label.max_y>y) label.max_y=y;
				if(label.min_y<y) label.min_y=y;
			}
		}
	}
	
	private void buildBlobs()
	{
		for(InnerLabel label : detectedLabels)
		{
			BlobSpace blob = new BlobSpace();
			
			blob.acc = label.acc;
			
			blob.x = label.min_x;
			blob.y = label.min_y;
			blob.size_x = label.max_x - label.min_x;
			blob.size_y = label.max_y - label.min_y;
			
			blob.label = -1;
			
			detectedBlobs.add(blob);
		}
	}

	boolean extractMaskFrom(BlobSpace blob, final Buffer8I image) {
		blob.mask = null;
		
		/*
		 * TODO
		 * Do a Mask of blob from original image
		 */
		
		return false;
	}
}
